package com.hexm.sudokuweb.sudoku.domain;

import com.hexm.sudokuweb.sudoku.app.SudokuDoService;
import com.hexm.sudokuweb.sudoku.util.SudokuUtil;

import java.util.Set;
import java.util.TreeSet;

/**
 * @Classname SudokuBo
 * @Description TODO
 * @Date 2021/2/11 15:01
 * @Created by hexm
 */
public class SudokuDo {

    private int[][] sudokuData = new int[9][9];

    public SudokuDo(int[][] sudokuData) {
        this.sudokuData = sudokuData;
    }


    public int[][] getSudokuData() {
        return sudokuData;
    }

    /**
     * 所有行是否合法
     *
     * @param checkType: 1-合法性检查 2-最终符合性检查
     * @return
     */
    public boolean isRowValid(int checkType) throws Exception {
        for (int i = 0; i < 9; i++) {
            if (1 == checkType) {
                if (!isValidGroup(sudokuData[i])) {
                    return false;
                }
            } else if (2 == checkType) {
                if (!isFullValidGroup(sudokuData[i])) {
                    return false;
                }
            } else {
                throw new Exception("invalid checkType");
            }
        }
        return true;
    }

    /**
     * 所有列是否合法
     *
     * @param checkType: 1-合法性检查 2-最终符合性检查
     * @return
     */
    public boolean isColumnValid(int checkType) throws Exception {
        int[] column = new int[9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                column[j] = sudokuData[j][i];
            }
            if (1 == checkType) {
                if (!isValidGroup(column)) {
                    return false;
                }
            } else if (2 == checkType) {
                if (!isFullValidGroup(column)) {
                    return false;
                }
            } else {
                throw new Exception("invalid checkType");
            }
        }
        return true;
    }

    /**
     * 所有9宫格数字是否合法
     *
     * @param checkType: 1-合法性检查 2-最终符合性检查
     * @return
     */
    public boolean isSquareValid(int checkType) throws Exception {
        for (int i = 0; i < 9; i = i + 3) {
            for (int j = 0; j < 9; j = j + 3) {
                int[] array = intsToArray(sudokuData[i][j], sudokuData[i][j + 1], sudokuData[i][j + 2], sudokuData[i + 1][j], sudokuData[i + 1][j + 1], sudokuData[i + 1][j + 2], sudokuData[i + 2][j], sudokuData[i + 2][j + 1], sudokuData[i + 2][j + 2]);
                if (1 == checkType) {
                    if (!isValidGroup(array)) {
                        return false;
                    }
                } else if (2 == checkType) {
                    if (!isFullValidGroup(array)) {
                        return false;
                    }
                } else {
                    throw new Exception("invalid checkType");
                }
            }
        }
//        if (!isValidGroup(intsToArray(sudokuData[0][0], sudokuData[0][1], sudokuData[0][2], sudokuData[1][0], sudokuData[1][1], sudokuData[1][2], sudokuData[2][0], sudokuData[2][1], sudokuData[2][2]))) {
//            return false;
//        }
//        if (!isValidGroup(intsToArray(sudokuData[0][3], sudokuData[0][4], sudokuData[0][5], sudokuData[1][3], sudokuData[1][4], sudokuData[1][5], sudokuData[2][3], sudokuData[2][4], sudokuData[2][5]))) {
//            return false;
//        }
        return true;
    }

    /**
     * 数字合并成数组
     *
     * @param i1
     * @param i2
     * @param i3
     * @param i4
     * @param i5
     * @param i6
     * @param i7
     * @param i8
     * @param i9
     * @return
     */
    private int[] intsToArray(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9) {
        int[] column = new int[9];
        column[0] = i1;
        column[1] = i2;
        column[2] = i3;
        column[3] = i4;
        column[4] = i5;
        column[5] = i6;
        column[6] = i7;
        column[7] = i8;
        column[8] = i9;
        return column;
    }

    /**
     * 是否合法单元（不存在重复数字）
     *
     * @param ints
     * @return
     */
    private boolean isValidGroup(int[] ints) {
//        Map<Integer, Integer> map = new HashMap<>();
        Set<Integer> set = new TreeSet<>();
        for (int i = 0; i < ints.length; i++) {
            if (ints[i] != 0) {
                if (set.contains(ints[i])) {
                    return false;
                } else {
                    set.add(ints[i]);
                }
            }
        }
        return true;
    }

    /**
     * 是否最终合法单元(1-9都存在且不重复）
     *
     * @param ints
     * @return
     */
    private boolean isFullValidGroup(int[] ints) {
        Set<Integer> set = new TreeSet<>();
        for (int i = 0; i < ints.length; i++) {
            set.add(ints[i]);
        }
        for (int i = 1; i <= 9; i++) {
            if (!set.contains(i)) {
                return false;
            }
        }
        return true;
    }



    /**
     * 是否完整数独
     *
     * @return
     * @throws Exception
     */
    public boolean isFullOk() throws Exception {
        return this.isColumnValid(2) && this.isRowValid(2) && this.isSquareValid(2);
    }

    /**
     * 是否合法（可继续计算直到找到完整解）
     *
     * @return
     */
    public boolean isValid() throws Exception {
        return this.isColumnValid(1) && this.isRowValid(1) && this.isSquareValid(1);
    }

    /**
     * 填充简单数字，剪枝
     */
    public void fillEasyDigit() {
//        int [][] old = SudokuUtil.dataCopy(this.getSudokuData());
        boolean filled = false;
        //1.检查所有行
        for (int i = 0; i < 9; i++) {
            int result = getEasyDigit(this.sudokuData[i]);
            if (result > 0) {
                for (int j = 0; j < 9; j++) {
                    if (sudokuData[i][j] == 0) {
                        sudokuData[i][j] = result;
                        filled = true;
                    }
                }
            }
        }
        //2.检查所有列
        int[] column = new int[9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                column[j] = sudokuData[j][i];
            }
            int result = getEasyDigit(column);
            if (result > 0) {
                for (int j = 0; j < 9; j++) {
                    if (sudokuData[j][i] == 0) {
                        sudokuData[j][i] = result;
                        filled = true;
                    }
                }
            }
        }
        //3.检查所有9宫格
        for (int i = 0; i < 9; i = i + 3) {
            for (int j = 0; j < 9; j = j + 3) {
                int[] array = intsToArray(sudokuData[i][j], sudokuData[i][j + 1], sudokuData[i][j + 2], sudokuData[i + 1][j], sudokuData[i + 1][j + 1], sudokuData[i + 1][j + 2], sudokuData[i + 2][j], sudokuData[i + 2][j + 1], sudokuData[i + 2][j + 2]);
                int result = getEasyDigit(array);
                if (result > 0) {
                    for (int ii = i; ii < i + 3; ii++) {
                        for (int jj = j; jj < j + 3; jj++) {
                            if (sudokuData[ii][jj] == 0) {
                                sudokuData[ii][jj] = result;
                                filled = true;
                            }
                        }
                    }
                }
            }
        }
//        if(filled) {
//            System.out.println("###############");
//            SudokuUtil.print(old);
//            System.out.println("---------------------");
//            SudokuUtil.print(this.getSudokuData());
//        }
    }

    /**
     * 找到轻松数字（9个数字中已出现8个，空余一个）
     *
     * @param array
     * @return
     */
    int getEasyDigit(int[] array) {
        Set<Integer> set = new TreeSet<>();
        for (int i = 0; i < 9; i++) {
            set.add(i + 1);
        }
        for (int i = 0; i < 9; i++) {
            if (set.contains(array[i])) {
                set.remove(array[i]);
            }
        }
        if (set.size() == 1) {
            return (int) set.toArray()[0];
        }
        return 0;
    }


    public static void main(String[] args) throws Exception {
        int[][] ints = new int[9][9];
        SudokuDo sudokuDo = new SudokuDo(ints);
        ints[0][0] = 1;
        ints[0][1] = 2;
        ints[0][2] = 3;
        ints[0][3] = 4;
        ints[0][4] = 5;
        ints[0][5] = 6;
        ints[0][6] = 7;
        ints[0][7] = 8;
        ints[0][8] = 9;
        System.out.println("sudokuDo.isRowValid(1):" + sudokuDo.isRowValid(1));
        System.out.println("sudokuDo.isColumnValid(1):" + sudokuDo.isColumnValid(1));
        System.out.println("sudokuDo.isSquareValid(1):" + sudokuDo.isSquareValid(1));
        System.out.println("sudokuDo.isRowValid(2):" + sudokuDo.isRowValid(2));
        System.out.println("sudokuDo.isColumnValid(2):" + sudokuDo.isColumnValid(2));
        System.out.println("sudokuDo.isSquareValid(2):" + sudokuDo.isSquareValid(2));
        boolean isValid = sudokuDo.isValidGroup(ints[1]);
        System.out.println(isValid);
        boolean isFullValid = sudokuDo.isFullValidGroup(ints[1]);
        System.out.println(isFullValid);
    }
}
